package com.example.rpapp.activity.command;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.WeightRandom;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.config.enums.RecordStatusEnum;
import com.example.config.exception.RLdCodeException;
import com.example.config.util.AssertUtil;
import com.example.config.util.SecurityUtil;
import com.example.rpapp.assembler.AwardAssembler;
import com.example.rpapp.assembler.RecordAssembler;
import com.example.rpapp.context.ActivityDrawContext;
import com.example.rpclient.dto.RecordAddCmd;
import com.example.rpclient.dto.data.*;
import com.example.rpclient.dto.query.RecordListByParamQuery;
import com.example.rpdomain.activity.ActivityEntity;
import com.example.rpdomain.activity.ActivityStatusEnum;
import com.example.rpdomain.activity.ActivityTime;
import com.example.rpdomain.award.AwardEntity;
import com.example.rpdomain.gateway.ActivityGateway;
import com.example.rpdomain.gateway.AwardGateway;
import com.example.rpdomain.gateway.PrizeGateway;
import com.example.rpdomain.gateway.RecordGateway;
import com.example.rpdomain.record.RecordEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


@Getter
@Slf4j
@Component
@AllArgsConstructor
public class DefaultDrawExe extends BaseDrawExe {

    private final RecordGateway recordGateway;
    private final AwardGateway awardGateway;
    private final PrizeGateway prizeGateway;
    private final TransactionTemplate transactionTemplate;


    @Override
    protected void addRecord(ActivityDrawContext context) {
        // 插入记录，默认记录可见
        if (Objects.isNull(context.getIsShow())) {
            context.setIsShow(Boolean.TRUE);
        }
        RecordAddCmd recordAddCmd = new RecordAddCmd();
        recordAddCmd.setUserId(SecurityUtil.getUserId());
        recordAddCmd.setActivityId(context.getActivityConfigVo().getActivityVo().getId());
        recordAddCmd.setActivityName(context.getActivityConfigVo().getActivityVo().getActivityName());
        recordAddCmd.setAwardId(context.getAwardVo().getId());
        recordAddCmd.setIsWinning(Boolean.TRUE.equals(context.getAwardEntity().isPrize()) ? 1 : 0);
        recordAddCmd.setState(context.getIsShow() ? RecordStatusEnum.STATUE_1.getValue() : RecordStatusEnum.STATUE_0.getValue());

        context.setRecordId(recordGateway.save(RecordAssembler.toAddEntity(recordAddCmd)).getId());
    }

    @Override
    protected DrawResultVo addRecordAndGetDrawResultVo(ActivityDrawContext context) {

        DrawResultVo resultVo = transactionTemplate.execute(status -> {

            DrawResultVo drawResultVo = null;
            try {
                addRecord(context);
                drawResultVo = getDrawResultVo(context.getAwardEntity());
            } catch (Exception e) {
                //错误处理
                status.setRollbackOnly();
                log.error("插入抽奖记录或封装抽奖结果失败！", e);
            }
            return drawResultVo;
        });

        AssertUtil.isTrue(Objects.isNull(resultVo), "抱歉访问人数过多稍后再来！");

        return resultVo;
    }

    @Override
    protected Boolean drawBefore(ActivityDrawContext context) {

        return transactionTemplate.execute(status -> {
            Boolean seccess = Boolean.TRUE;
            int update = 0;
            try {
                // 这里需要优化
                // 扣减库存
                update = awardGateway.deductionAwardNumber(context.getAwardVo().getId(), 1);
                AssertUtil.isTrue(update != 1, "扣减库存失败！");
                addRecord(context);
            } catch (Exception e) {
                //错误处理
                status.setRollbackOnly();
                if (update > 0){
                    // 回退库存
                    awardGateway.deductionAwardNumber(context.getAwardVo().getId(), -1);
                }
                log.error("扣减库存和插入记录出错", e);
                seccess = Boolean.FALSE;
            }
            return seccess;
        });
    }

    @Override
    protected DrawResultVo getDrawResultVo(AwardEntity awardEntity) {
        DrawResultVo drawResultVO = new DrawResultVo();
        drawResultVO.setAwardName(awardEntity.getAwardName());
        if (Objects.nonNull(awardEntity.getPrizeId()) && !awardEntity.getPrizeId().toString().equals("0")) {
            drawResultVO.setPrizeName(prizeGateway.one(awardEntity.getPrizeId()).getPrizeName());
        }
        drawResultVO.setIsDraw(awardEntity.isPrize());
        return drawResultVO;
    }

    @Override
    protected AwardVo getAward(List<AwardVo> awardVOList) {
        // 抽奖算法
        /*
        1、权重
         */
        List<WeightRandom.WeightObj<AwardVo>> weightList = new ArrayList<>();
        awardVOList.forEach(item -> weightList.add(new WeightRandom.WeightObj<>(item, item.getProbability())));
        //创建带有权重的随机生成器
        WeightRandom<AwardVo> wr = RandomUtil.weightRandom(weightList);
        return wr.next();
    }

    @Override
    protected void checkActivityRule(ActivityConfigVo activityConfigVo) {
        List<RuleVo> ruleVoList = activityConfigVo.getRuleVoList();
        if (CollUtil.isEmpty(ruleVoList)) {
            return;
        }
        // 获取活动第一个规则
        RuleVo ruleVo = ruleVoList.get(0);

        final var query = new RecordListByParamQuery();
        query.setUserId(SecurityUtil.getUserId());
        query.setActivityId(activityConfigVo.getActivityVo().getId());
        query.setPageSize(1000);
        IPage<RecordEntity> page = recordGateway.page(query);

        // 校验最大参与次数
        AssertUtil.isTrue(page.getRecords().size() >= ruleVo.getMaxJoinNumber(), "你已达到活动最大参与次数，不可参与！");

        List<RecordEntity> winningRecordList = page.getRecords()
                .stream().filter(item -> item.getIsWinning() == 1)
                .collect(Collectors.toList());

        // 校验最大中奖次数
        AssertUtil.isTrue(winningRecordList.size() >= ruleVo.getMaxWinningNumber(), "你已达到最大中奖次数，不可参与！");
    }

    @Override
    protected void checkActivityTime(ActivityVo activityVo) {
        ActivityEntity activityEntity = new ActivityEntity();
        activityEntity.setActivityTime(new ActivityTime(activityVo.getStartTime(), activityVo.getEndTime()));
        ActivityStatusEnum activityStatus = activityEntity.getActivityTime().getStatus();
        if (!ActivityStatusEnum.START.equals(activityStatus)) {
            throw new RLdCodeException(String.format("活动%s", activityStatus.getDescription()));
        }
    }
}
